#ifndef __PENDIENTES_H__
#define __PENDIENTES_H__

#ifndef NULL
#define NULL 0
#endif

#include <iostream>
#include "lista.h"
#include "catalogo.h"
#include "defines.h"

using namespace std;

class Pendientes{
	private:
		struct nodoCola;

		typedef Lista<nodoCola>::IterLista nodoMat;

		struct nodoCola{
			nodoCola(vivienda v, Lista<Lista<nodoMat>::IterLista> nEM):viv(v),nodosEnMateriales(nEM){};
			~nodoCola(){};
			vivienda viv;
			Lista<Lista<nodoMat>::IterLista> nodosEnMateriales;
		};
		Lista<nodoCola> _cola;
		const Catalogo& _catalogo;
		Lista<nodoMat>* _porMaterial;
		
		Pendientes& operator=(const Pendientes&);

	public:
		class iterPendientes{
			public:
				iterPendientes();
				//iterPendientes(Pendientes*,material);
				iterPendientes(const Pendientes&,material);
				~iterPendientes();
				bool tieneProximo() const;
				vivienda actual() const;
				void avanzar();
				void eliminar();
			private:
				Lista<nodoMat>::IterLista it;
		};
		//Pendientes(const Catalogo& catalogo);
		//Pendientes(); no puede construirse un pendientes con un catalogo vacio.
		Pendientes(const Catalogo& catalogo, nat n);
		~Pendientes();
		void encolar(vivienda v);
		void desencolar(); // No esta en el diseño, pero queda mucho mas prolijo (thomas)
		Lista<vivienda> cola() const;
		const Catalogo& catalogo() const;
		bool esta(vivienda v) const;
		bool hayPendiente() const;
		vivienda primera() const;
		iterPendientes crearIterPendientes(material); //No se usa porque necesariamente tenes que pasarle _pendientes, no puede ser cualquiera.
		void mostrar(ostream&) const;
};


//Pendientes::Pendientes(const Catalogo& c):_cola(Lista<nodoCola>()), _catalogo(c), _porMaterial(NULL){}
//Pendientes::Pendientes():_cola(Lista<nodoCola>()),_catalogo(Catalogo()), _porMaterial(NULL){}

Pendientes::Pendientes(const Catalogo& c, nat n):_cola(Lista<nodoCola>()),_catalogo(c),_porMaterial(new Lista<nodoMat>[n+1]){
	for(int i=0;i<=n;i++){
		_porMaterial[i] = Lista<nodoMat>(); // hace falta?? (thomas)
	}
}

Pendientes::~Pendientes(){
	delete [] _porMaterial;
}

void Pendientes::encolar(vivienda v){
	assert(v<_catalogo.cantidadViviendas());
	_cola.agregarAtras(nodoCola(v,Lista<Lista<nodoMat>::IterLista>()));
	Multiconj_materiales::IterMateriales it=_catalogo.crearIterMateriales(v);
	Lista<nodoCola>::IterLista posEnCola = _cola.crearItAlUlt();
	while(it.tieneProximo()){
		material actual = it.avanzar();
		_porMaterial[actual].agregarAtras(posEnCola);
		_cola.ultimo().nodosEnMateriales.agregarAtras(_porMaterial[actual].crearItAlUlt());
	}
}

void Pendientes::desencolar(){
	assert(!_cola.esVacia());
	Lista<nodoCola>::IterLista it = _cola.crearIt();
	Lista<Lista<nodoMat>::IterLista>::IterLista iterMat = it.actual().nodosEnMateriales.crearIt();
	while(iterMat.tieneProximo()){
		iterMat.actual().eliminar();
		iterMat.avanzar();
	}
	it.eliminar();
}

Lista<vivienda> Pendientes::cola() const{
	Lista<vivienda> res;
	Lista<nodoCola>::IterLista_const it=_cola.crearIt_const();
	while(it.tieneProximo()){
		res.agregarAtras(it.actual().viv);
		it.avanzar();
	}
	return res;
}

const Catalogo& Pendientes::catalogo() const{
	return _catalogo;
}

bool Pendientes::esta(vivienda v) const{
	Lista<nodoCola>::IterLista_const it=_cola.crearIt_const();
	while(it.tieneProximo()){
		if(it.actual().viv==v)return true;
		it.avanzar();
	}
	return false;
}

bool Pendientes::hayPendiente() const{
	return !_cola.esVacia();
}

vivienda Pendientes::primera() const{
	assert(!_cola.esVacia());
	return _cola.primero().viv;
}

void Pendientes::mostrar(ostream& os) const{
	os << cola();
}

Pendientes::iterPendientes Pendientes::crearIterPendientes(material m){
	assert(m<=_catalogo.cantMateriales());
	return iterPendientes(*this,m);
}

Pendientes::iterPendientes::iterPendientes(){}

Pendientes::iterPendientes::iterPendientes(const Pendientes& p, material m) : it(p._porMaterial[m].crearIt()){}

Pendientes::iterPendientes::~iterPendientes(){}

bool Pendientes::iterPendientes::tieneProximo() const{
	return it.tieneProximo();
}

vivienda Pendientes::iterPendientes::actual() const{
	return it.actual().actual().viv;
}

void Pendientes::iterPendientes::avanzar(){
	it.avanzar();
}

void Pendientes::iterPendientes::eliminar(){
	assert(it.tieneProximo());
	Lista<nodoCola>::IterLista iterVivienda = it.actual();
	it.avanzar();
	//cout << "actual antes de eliminar: " << actual() << endl;
	Lista<Lista<nodoMat>::IterLista>::IterLista iterMat = iterVivienda.actual().nodosEnMateriales.crearIt();
	while(iterMat.tieneProximo()){
		iterMat.actual().eliminar();
		iterMat.avanzar();
	}
	iterVivienda.eliminar();
}

ostream& operator<<(ostream& out, const Pendientes& p) {
	p.mostrar(out);
	return out;
}

#endif
